package com.sxg.webSocket;

import com.sxg.system.service.ISysUserService;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassDescription: websocket服务端
 */
@ConditionalOnClass(value = WebsocketConfig.class)
@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
@EnableAsync
public class WebsocketServer {

    private Session session;
    private static Long userId;

    private TaskScheduler taskScheduler;

    private ISysUserService iSysUserService;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    /**
     * 记录在线连接客户端数量
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * concurrent包的线程安全map，用来存放每个客户端对应的MyWebSocket对象。
     */
    private static ConcurrentHashMap<Long, WebsocketServer> wsServerMap = new ConcurrentHashMap<>();

    /**
     * 定时任务集合
     */
    Map<String, java.util.concurrent.ScheduledFuture<?>> stringScheduledFutureMap = new ConcurrentHashMap<>();


    /**
     * 此处是解决无法注入的关键
     */
    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebsocketServer.applicationContext = applicationContext;
    }


    /**
     * 服务端与客户端连接成功时执行
     *
     * @param session 会话
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Long userId) {
        //初始化
        init();
        this.session = session;
        this.setUserId(userId);
        log.info("用户{}连接成功", userId);
        //接入的客户端+1
        int count = onlineCount.incrementAndGet();
        //集合中存入客户端对象+1
        wsServerMap.put(userId, this);
        //开启一个定时任务
        Trigger trigger = new CronTrigger("0/5 * * * * *");
        java.util.concurrent.ScheduledFuture<?> schedule = taskScheduler.schedule(new MyRunTask(session, userId, iSysUserService), trigger);
        //根据用户id将定时任务线程存到map中
        stringScheduledFutureMap.put(userId.toString(), schedule);
        log.info("与客户端连接成功，当前连接的客户端数量为：{}", count);
    }


    /**
     * 初始化bean （解决websocket不被容器管理无法注入bean的问题）
     */
    private void init() {
        if (taskScheduler == null) {
            taskScheduler = applicationContext.getBean(TaskScheduler.class);
        }
        if (iSysUserService == null) {
            iSysUserService = applicationContext.getBean(ISysUserService.class);
        }
    }

    /**
     * 收到客户端的消息时执行
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自客户端的消息，客户端地址：{}，消息内容：{}", session.getMessageHandlers(), message);
    }

    /**
     * 连接发生报错时执行
     *
     * @param session   会话
     * @param throwable 报错
     */
    @OnError
    public void onError(Session session, @NonNull Throwable throwable) {
        log.error("连接发生报错");
        this.onClose();
        throwable.printStackTrace();
    }

    /**
     * 连接断开时执行
     */
    @OnClose
    public void onClose() {
        //接入客户端连接数-1
        int count = onlineCount.decrementAndGet();
        //集合中的客户端对象-1
        wsServerMap.remove(this.getUserId());
        //定时任务-1
        java.util.concurrent.ScheduledFuture<?> scheduledFuture = stringScheduledFutureMap.get(this.getUserId().toString());
        scheduledFuture.cancel(true);
        stringScheduledFutureMap.remove(this.getUserId().toString());
        log.info("服务端断开连接，当前连接的客户端数量为：{}", count);
    }


    /**
     * 向客户端推送消息
     *
     * @param message 消息
     */
    public void sendMessage(String message) {
        log.info("推送消息给客户端:{}，消息内容为：{}", this.session.getMessageHandlers(), "666");
    }


    /**
     * 群发消息
     *
     * @param message 消息
     */
    public void sendMessageToAll(String message) {
        ConcurrentHashMap<Long, WebsocketServer> map = wsServerMap;
        map.forEach((k, v) -> {
            v.session.getAsyncRemote().sendText(message);
        });
    }

}

